package com.ts.api.module.api.wake.impl;

import com.gitee.apanlh.util.base.MapUtils;
import com.gitee.apanlh.util.cache.local.Cache;
import com.gitee.apanlh.util.cache.local.CacheUtils;
import com.ts.api.module.api.wake.WakeRatioService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class WakeRatioServiceImpl implements WakeRatioService {

    @Autowired
    private RedissonClient redissonClient;

    private final Cache<String, Integer> WAKE_RATIO_CACHE = CacheUtils.cache(MapUtils.newHashMap());
    private final Cache<String, SmoothWeightedRoundRobin<Boolean>> SWRR_CACHE = CacheUtils.cache(MapUtils.newHashMap());

    @Override
    public boolean wake(String key, int wakeRatio, boolean canWake) {
        RLock lock = redissonClient.getLock("custom:wake_lock:" + key);
        lock.lock(10, TimeUnit.SECONDS);
        try {
            return wake0(key, wakeRatio, canWake);
        } finally {
            lock.unlock();
        }
    }

    private boolean wake0(String key, int wakeRatio, boolean canWake) {
        Objects.requireNonNull(key);
        Integer oldWakeRatio = this.WAKE_RATIO_CACHE.get(key);

        if (oldWakeRatio == null || oldWakeRatio.intValue() != wakeRatio) {
            this.WAKE_RATIO_CACHE.put(key, wakeRatio);
            int successRatio = wakeRatio;
            if (successRatio > 100) {
                successRatio = 100;
            }
            if (successRatio < 0) {
                successRatio = 0;
            }
            int failRatio = 100 - successRatio;
            SmoothWeightedRoundRobin<Boolean> swrr = new SmoothWeightedRoundRobin<>();
            swrr.add(successRatio, true);
            swrr.add(failRatio, false);
            this.SWRR_CACHE.put(key, swrr);
        }

        SmoothWeightedRoundRobin<Boolean> swrr = this.SWRR_CACHE.get(key);
        if (canWake) {
            return swrr.next();
        } else {
            swrr.designatedNext(false);
            return false;
        }
    }

}
